#pragma once
#ifndef _ABSTRACT_SOLVER_
#define _ABSTRACT_SOLVER_

//================================================================================================================================
//================================================================================================================================
// QtCore
#include <QtCore/QList>
#include <QtCore/QString>
// Локальные
#include "material.h"
#include "linal/vector.h"
#include "linal/csrmatrix.h"
#include "linal/slaesolver.h"
#include "geometry/2d/vector.h"
#include "fem/trianglenet.h"
#include "fem/trianglenetsearch.h"

//================================================================================================================================
// Абстрактный решатель. Реализует функции не зависящие от конкретной задачи.
//================================================================================================================================
class AbstractSolver
{
public:
    //****************************************************************************************************************************
    //****************************************************************************************************************************
    AbstractSolver();
    virtual ~AbstractSolver() {delete m_NetSearcher;}

    //****************************************************************************************************************************
    //****************************************************************************************************************************
    // Непосредственно решение задачи.
    void solve();

    // Постобработка результатов.
    void postproc();

protected:
    //Различные настройки программы.
    enum SubTask
    {
        NoSubTasks      = 0x00,
        Debug           = 0x01,     // Обеспечить вывод дополнительной информации для отладки.
        Convection      = 0x02,     // Учитывать конвекции.
        Solidification  = 0x04,     // Учет фазового перехода.
        Stationarity    = 0x08,     // Решать стационарную задачу.
        Rotation        = 0x10,     // Вращение тигля вокруг оси.
        Concentration   = 0x20      // Учет концентрации.
    };
    Q_DECLARE_FLAGS(SubTasks, SubTask)

    // Возможные состояния процесса кристаллизации.
    // Если на текущем шаге по времени не обнаружен фронт кристаллизации, то реакция системы зависит от текущего состояния:
    // если система была в состоянии JastStarted, то считаем, что зародившийся кристалл весь расплавился и система перешла в
    // состояние NotStarted, а если система была в состоянии InProgress, то считаем, что кристаллизация завершена и система
    // перешла в состояние Finished.
    enum CrystallizationState
    {
        NotStarted,                 // Кристаллизация еще не началась.
        JustStarted,                // Кристализация началась только что.
        InProgress,                 // Кристаллизация происходит в текущий момент.
        Finished                    // Весь расплав закристаллизовался.
    };

    // Способы численного взятия производной.
    enum DiffMethod
    {
        LDiff,  // Левая разность.
        RDiff   // Правая разность.
    };

    // Переменная, по которой нужно взять производную.
    enum DiffVar
    {
        R,
        Z
    };

    //****************************************************************************************************************************
    //****************************************************************************************************************************
    //****************************************************************************************************************************
    //****************************************************************************************************************************
    void solveStationary();
    void solveNonStationary();

    //****************************************************************************************************************************
    // Профили компонент скорости.
    //****************************************************************************************************************************
    void profileVr(QList<double> r_sections, const QString &filename, const QString &in_path, const QString &out_path);
    void profileVz(QList<double> z_sections, const QString &filename, const QString &in_path, const QString &out_path);

    //****************************************************************************************************************************
    // Математические операции.
    //****************************************************************************************************************************
    // Производная по направлению в точке.
    double derivation(const linal::Vector &f, int point, DiffVar var, DiffMethod method);

    //****************************************************************************************************************************
    // Функции чтения/записи.
    //****************************************************************************************************************************
    // Запись результата расчетов в файл.
    void writeResult(int time_layer);

    // Запись различной информации о расчетной области и краевых условиях нужной для возобновления расчетов с любого места.
    void writeResumeInfo(int time_layer);

    // Чтение различной информации о расчетной области и краевых условиях нужной для возобновления расчетов с указанного места.
    void readResumeInfo();

    //****************************************************************************************************************************
    // Постобработка данных.
    //****************************************************************************************************************************
    // Перевод бинарных данных в текстовый вид.
    void bin2txt();

    // Динамика роста массы кристалла.
    void crystalMass();

    // Расчет кинетической энергии расплава
    void kineticEnergy();

    // Построение профилей скорости.
    void speedProfiles();

    // Построение фронта кристаллизации.
    void crystallizationFront();

    // Построение профилей температуры.
    void temperatureProfiles();

    // Построение локальных конвективных тепловых потоков.
    void localConvectiveHeatFlow();

    // Построение локальных кондуктивных тепловых потоков.
    void localConductiveHeatFlow();

    virtual void limitsForR(double z, double *r_min, double *r_max, int *num_of_points, bool meltOnly) = 0;
    virtual void limitsForZ(double r, double *z_min, double *z_max, int *num_of_points, bool meltOnly) = 0;

    //****************************************************************************************************************************
    // Формирование конечноэлементных СЛАУ и краевых условий. Решение СЛАУ.
    //****************************************************************************************************************************
    virtual void formV() = 0;

    void formCSLAE(int time_layer);
    void formTSLAE(int time_layer);
    void formVfSLAE(int time_layer);
    void formPsiSLAE(int time_layer);
    void formOmegaSLAE(int time_layer);

    virtual void cBC(int time_layer) = 0;
    virtual void tBC(int time_layer) = 0;
    virtual void vfBC(int time_layer) = 0;
    virtual void psiBC(int time_layer) = 0;
    virtual void omegaBC(int time_layer) = 0;

    bool solveSLAE(const linal::CSRMatrix &A, const linal::Vector &f, linal::Vector &x);

    //****************************************************************************************************************************
    // Построение/перестройка сетки.
    //****************************************************************************************************************************
    // Создание сетки по умолчанию (на начальный момент времени).
    virtual bool createNet() = 0;

    // Создание сетки с учетом продвижения фронта.
    virtual bool recreateNet() = 0;

    //****************************************************************************************************************************
    // Функции выделения фронта. Ее работа заключается в поиске изотермы с соответствющей температурой и отсечении той части
    // изотермы, которая не находится в расплаве. Температура изотермы не задается строго, т.к. для модели сосредоточенного
    // источника фронт идентифицируется изотермой с температурой равной температуре кристаллизации (Ts), а для модели с
    // распределенным источником - двумя изотермами с температурой Ts+dT и Ts-dT, то для унификации использования данных функций.
    // Вторая функция, в случае если кристалл покрывает не все дно тигля, добовлет к фронту участок дна тигля, контактирующий
    // с расплавом.
    //****************************************************************************************************************************
    virtual QList<geometry::_2d::Point> identifyFront(double temperature) = 0;
    virtual QList<geometry::_2d::Point> identifyFrontExt(double temperature) = 0;

    //****************************************************************************************************************************
    //****************************************************************************************************************************
    //****************************************************************************************************************************
    //****************************************************************************************************************************

    //****************************************************************************************************************************
    //Физические и геометрические параметры задачи.
    //****************************************************************************************************************************
    QList<Material> m_Materials;    // Список материалов расчетной области.
    double m_TGrad;                 // Градиент температуры в печи.
    double m_TMax;                  // Температура в горячей зоне печи.
    double m_TRod;                  // Температура державки.
    double m_V;                     // Скорость опускания тигля.
    double m_VRot;                  // Скорость вращения тигля вокруг своей оси.
    double m_Radius;                // Радиус тигля.

    //****************************************************************************************************************************
    // Математические параметры задачи.
    //****************************************************************************************************************************
    int m_MaxiterSLAE;              // Максимальное число итераций, отведенное на решение СЛАУ.
    int m_MaxiterNonlin;            // Максимальное число итераций, отведенное на решение нелинейной задачи.
    double m_EpsSLAE;               // Невязка, достаточная для того, чтобы объявить решение СЛАУ найденным.
    double m_EpsNonlin;             // Невязка, достаточная для того, чтобы объявить решение нелинейной задачи найденным.
    double m_DiffStep;              // Шаг, используемый для численного нахождения производных.
    double m_FrontHalfWidth;        // Полуширина фронта кристаллизации(в градусах).
    double m_W;                     // Коэффициент релаксации.
    double m_MinCrystalHeight;      // Минимальная высота кристалла.
    QList<linal::SLAESolver*> m_Solvers;    // Список решателей СЛАУ.

    //****************************************************************************************************************************
    // Пространственная сетка.
    //****************************************************************************************************************************
    fem::TriangleNet m_Net;         // Конечноэлементная сетка.
    fem::TriangleNetSearch *m_NetSearcher;  // Класс, обеспечивающий поиск по конечноэлементной сетке.

    //****************************************************************************************************************************
    // Временная сетка.
    //****************************************************************************************************************************
    double m_TimeStep;              // Шаг по времени.
    int m_TimeMoment;               // Момент времени для которого решается нестационарная задача.
    int m_NumOfTimeLayers;          // Число шагов по времени.

    //****************************************************************************************************************************
    // Значения искомых функций в узлах сетки
    //****************************************************************************************************************************
    linal::Vector m_C;              // Значение концентрации.
    linal::Vector m_T;              // Значение температуры.
    linal::Vector m_Psi;            // Значение функции тока.
    linal::Vector m_Omega;          // Значение вихря.
    linal::Vector m_Vr;             // Значение радиальной компоненты скорости конвективного течения.
    linal::Vector m_Vz;             // Значение осевой компоненты скорости конвективного течения.
    linal::Vector m_Vf;             // Значение окружной компоненты скорости конвективного течения.

    linal::Vector m_TOldNolin;      // Значение температуры с предыдущей итерации по нелинейности.
    linal::Vector m_VfOldNolin;     // Значение окружной компоненты скорости с предыдущей итерации по нелинейности.
    linal::Vector m_PsiOldNolin;    // Значение функции тока с предыдущей итерации по нелинейности.
    linal::Vector m_OmegaOldNolin;  // Значение вихря с предыдущей итерации по нелинейности.

    linal::Vector m_COldTime;       // Значение концентрации с предыдущей итерации по времени.
    linal::Vector m_TOldTime;       // Значение температуры с предыдущей итерации по времени.
    linal::Vector m_VfOldTime;      // Значение окружной компоненты скорости с предыдущей итерации по времени.
    linal::Vector m_OmegaOldTime;   // Значение вихря с предыдущей итерации по времени.

    //****************************************************************************************************************************
    // Фронт кристаллизации.
    //****************************************************************************************************************************
    QList<int> m_Front;             // Фронт с текущей итерации. Хранится в виде индексов точек конечноэлементной сетки.
    QList<int> m_FrontPlus;
    QList<int> m_FrontMinus;
    CrystallizationState m_State;   // Состояние процесса кристаллизации.

    //****************************************************************************************************************************
    // Матрица и вектор правой части СЛАУ
    //****************************************************************************************************************************
    linal::CSRMatrix m_A;           // Матрица СЛАУ.
    linal::Vector m_F;              // Вектор правой части.

    //****************************************************************************************************************************
    // Сервисные настройки(настройки не связанные непосредственно с процессом моделирования).
    //****************************************************************************************************************************
    int m_ResumeFrom;               // Продолжение решения с заданной итерации.
    int m_OutputStep;               // Переодичность вывода результатов (в итерациях).
    QString m_OutputDir;            // Директория для записи результатов.
    QString m_InputDir;             // Директория для считывания данных.
    SubTasks m_SubTasks;            // Различные опции-флаги.

    //****************************************************************************************************************************
    // Краевые условия.
    //****************************************************************************************************************************
    QList<int> m_TopTBC;            // Номера узлов конечноэлементной сетки, в которых задано КУ первого рода для температуры.
    QList<int> m_TypeTBC;           // Номера формул, по которым выисляеются значения КУ первого рода в соответствующих узлах сетки.
    QList<int> m_TopVfBC;           // Номера узлов конечноэлементной сетки, в которых задано КУ первого рода для окружной скорости.
    QList<int> m_TypeVfBC;          // Номера формул, по которым выисляеются значения КУ первого рода в соответствующих узлах сетки.
    QList<int> m_TopPsiBC;          // Номера узлов конечноэлементной сетки, в которых задано КУ первого рада для функции тока.
    QList<int> m_TypePsiBC;         // Номера формул, по которым выисляеются значения КУ первого рода в соответствующих узлах сетки.
    QList<int> m_TopOmegaBC;        // Номера узлов конечноэлементной сетки, в которых задано КУ первого рада для функции вихря.
    QList<int> m_TypeOmegaBC;       // Номера формул, по которым выисляеются значения КУ первого рода в соответствующих узлах сетки.
};

#endif // _ABSTRACT_SOLVER_
